﻿using DatabaseModel;
using GuiSearcher.Enums;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using GuiSearcher.Extensions;
using System.Linq.Dynamic;

namespace GuiSearcher.Model {
    public class LogBookEntryReporitory {
        
        public static List<LogBookEntry> GetListBySearchCriteria(SearchCriteria searchCriteria) {

            using(ElliContext context = new ElliContext()) {
                
                DbSet<LogBookEntry> dbSet = context.LogBookEntries;
                IQueryable<LogBookEntry> queryable = dbSet.AsQueryable<LogBookEntry>();
                queryable = Filter(searchCriteria, queryable);
                return queryable.ToList();
            }
        }




        private static IQueryable<LogBookEntry> Filter(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {

            string test = "this AND this2 OR this3";
            string[] splitters = new[] { "AND", "OR" };
            List<string> parts = new List<string>();
            string[] result = test.Split(splitters, out parts);


            queryable = FilterByTemperature(searchCriteria, queryable);
            queryable = FilterByAoi(searchCriteria, queryable);
            queryable = FilterByPola(searchCriteria, queryable);
            queryable = FilterByScans(searchCriteria, queryable);
            queryable = FilterByResolution(searchCriteria, queryable);
            queryable = FilterByDateTime(searchCriteria, queryable);
            queryable = FilterBySubject(searchCriteria, queryable);
            queryable = FilterByMacro(searchCriteria, queryable);
            return queryable;
        }

        private static IQueryable<LogBookEntry> FilterBySubject(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {

            if (!string.IsNullOrEmpty(searchCriteria.Subject)) {
                if (searchCriteria.IsSubjectRegex) {
                    return queryable.Where("subject.Contains(@0) or subject.Contains(@1)", "Bi", "Seo");
                 //   return queryable.Where(lbe => searchCriteria.Subject.Contains(lbe.subject));
                }
                else {
                    return queryable.Where(lbe => searchCriteria.Subject == lbe.subject);
                }

            }
            else {
                return queryable;
            }

            
        }



        private static string FilterByStrings(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            string sqlQuery = queryable.ToString();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(sqlQuery);
            if (!string.IsNullOrEmpty(searchCriteria.Macro) || !string.IsNullOrEmpty(searchCriteria.Subject)) {
                if (sqlQuery.IndexOf("WHERE") == -1) {
                    stringBuilder.Append(" WHERE ");
                }


                if (!string.IsNullOrEmpty(searchCriteria.Subject)) {
                    stringBuilder.Append(string.Format(" AND ({0})", GetWhereQueryForAttribute(searchCriteria.Subject, "subject")));
                }

                if (!string.IsNullOrEmpty(searchCriteria.Macro)) {
                    stringBuilder.Append(string.Format(" AND ({0})", GetWhereQueryForAttribute(searchCriteria.Macro, "macro")));
                }
            }
            
            

            

           // queryable = context.LogBookEntries.SqlQuery(GetWhereQueryForAttribute(searchCriteria.Subject, "subject")).AsQueryable<LogBookEntry>();
                   
                

                //if (!string.IsNullOrEmpty(searchCriteria.Subject) && searchCriteria.IsSubjectRegex) {
                //    queryable = queryable.Where(e => e.subject.Contains(searchCriteria.Subject));
                //}

                //if (!string.IsNullOrEmpty(searchCriteria.Subject) && !searchCriteria.IsSubjectRegex) {
                //    queryable = queryable.Where(e => e.subject == searchCriteria.Subject);
                //}



            return stringBuilder.ToString();
        }

        private static string GetWhereQueryForAttribute(string inputString, string attribute) {
            if (string.IsNullOrEmpty(inputString)) {
                return string.Empty;
            }
            List<string> splittedKeywords = new List<string>();
            string[] partsToSplit = MySqlKeywordsEnumWrapper.GetStringArray();
            string[] splittedString = inputString.Split(partsToSplit, out splittedKeywords);
            for (int i = 0; i < splittedString.Length; i++) {
                splittedString[i] = AddLikeKeywordForAttribute(splittedString[i], attribute);
            }

            string result = splittedString.Glue(splittedKeywords.ToArray());
            
            return result;
        }

        private static string AddLikeKeywordForAttribute(string input, string attribute) {
            Regex regexLeftBrace = new Regex(@"[^\(]*\(*(.*)");
            Regex regexRightBrace = new Regex(@"([^\)]*)");
            Match match;
            if (input.IndexOf(@"(") != -1) {
                match = regexLeftBrace.Match(input);
                
            }
            else {
                match = regexRightBrace.Match(input);
            }

            string trimedMatch = match.Groups[1].ToString().Trim();
            return input.Replace(trimedMatch, string.Format("{0} like '%{1}%'", attribute, trimedMatch));

        }


        private static IQueryable<LogBookEntry> FilterByMacro(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (!string.IsNullOrEmpty(searchCriteria.Macro) && searchCriteria.IsMacroRegEx) {
                queryable = queryable.Where(e => e.macro.Contains(searchCriteria.Macro));
            }

            if (!string.IsNullOrEmpty(searchCriteria.Subject) && !searchCriteria.IsSubjectRegex) {
                queryable = queryable.Where(e => e.macro == searchCriteria.Macro);
            }
            return queryable;
        }


        private static IQueryable<LogBookEntry> FilterByTemperature(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.Temperature != null) {
                switch (searchCriteria.TemperatureSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.temperature == searchCriteria.Temperature.Value);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.temperature < searchCriteria.Temperature.Value);
                        break;
                    case SearchModeEnum.GREATER:
                        queryable = queryable.Where(e => e.temperature > searchCriteria.Temperature.Value);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => e.temperature <= searchCriteria.Temperature.Value);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.temperature >= searchCriteria.Temperature.Value);
                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        queryable = queryable.Where(e => e.temperature != searchCriteria.Temperature.Value);
                        break;
                }
            }
            return queryable;
        }

        private static IQueryable<LogBookEntry> FilterByAoi(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.Aoi != null) {
                switch (searchCriteria.AoiSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.aoi == searchCriteria.Aoi.Value);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.aoi < searchCriteria.Aoi.Value);
                        break;
                    case SearchModeEnum.GREATER:
                        queryable = queryable.Where(e => e.aoi > searchCriteria.Aoi.Value);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => e.aoi <= searchCriteria.Aoi.Value);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.aoi >= searchCriteria.Aoi.Value);
                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        queryable = queryable.Where(e => e.aoi != searchCriteria.Aoi.Value);
                        break;
                }
            }
            return queryable;
        }


        private static IQueryable<LogBookEntry> FilterByPola(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.Pola != null) {
                switch (searchCriteria.PolaSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.pola == searchCriteria.Pola.Value);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.pola < searchCriteria.Pola.Value);
                        break;
                    case SearchModeEnum.GREATER:
                        queryable = queryable.Where(e => e.pola > searchCriteria.Pola.Value);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => e.pola <= searchCriteria.Pola.Value);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.pola >= searchCriteria.Pola.Value);
                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        queryable = queryable.Where(e => e.pola != searchCriteria.Pola.Value);
                        break;
                }
            }
            return queryable;
        }


        private static IQueryable<LogBookEntry> FilterByScans(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.Scans != null) {
                switch (searchCriteria.ScansSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.scans == searchCriteria.Scans.Value);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.scans < searchCriteria.Scans.Value);
                        break;
                    case SearchModeEnum.GREATER:
                        queryable = queryable.Where(e => e.scans > searchCriteria.Scans.Value);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => e.scans <= searchCriteria.Scans.Value);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.scans >= searchCriteria.Scans.Value);
                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        queryable = queryable.Where(e => e.scans != searchCriteria.Scans.Value);
                        break;
                }
            }
            return queryable;
        }


        private static IQueryable<LogBookEntry> FilterByResolution(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.Resolution != null) {
                switch (searchCriteria.ResolutionSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.resolution == searchCriteria.Resolution.Value);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.resolution < searchCriteria.Resolution.Value);
                        break;
                    case SearchModeEnum.GREATER:
                        queryable = queryable.Where(e => e.resolution > searchCriteria.Resolution.Value);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => e.resolution <= searchCriteria.Resolution.Value);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.resolution >= searchCriteria.Resolution.Value);
                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        queryable = queryable.Where(e => e.resolution != searchCriteria.Resolution.Value);
                        break;
                }
            }
            return queryable;
        }


        private static IQueryable<LogBookEntry> FilterByDateTime(SearchCriteria searchCriteria, IQueryable<LogBookEntry> queryable) {
            if (searchCriteria.DateTime != null && searchCriteria.DateTime != DateTime.MinValue) {
                switch (searchCriteria.DateTimeSearchMode) {
                    case SearchModeEnum.EQUAL:
                        queryable = queryable.Where(e => e.date.Day == searchCriteria.DateTime.Day && 
                            e.date.Month == searchCriteria.DateTime.Month &&
                            e.date.Year == searchCriteria.DateTime.Year);
                        break;
                    case SearchModeEnum.LOWER:
                        queryable = queryable.Where(e => e.date < searchCriteria.DateTime);
                        break;
                    case SearchModeEnum.GREATER:
                        DateTime date = searchCriteria.DateTime.AddDays(1);
                        queryable = queryable.Where(e => e.date >= date);
                        break;
                    case SearchModeEnum.LOWER_OR_EQUAL:
                        queryable = queryable.Where(e => (e.date.Day == searchCriteria.DateTime.Day && 
                            e.date.Month == searchCriteria.DateTime.Month &&
                            e.date.Year == searchCriteria.DateTime.Year) || 
                            e.date < searchCriteria.DateTime);
                        break;
                    case SearchModeEnum.GREATER_OR_EQUAL:
                        queryable = queryable.Where(e => e.date >= searchCriteria.DateTime);

                        break;
                    case SearchModeEnum.NOT_EQUAL:
                        DateTime min = new DateTime(searchCriteria.DateTime.Year, searchCriteria.DateTime.Month, searchCriteria.DateTime.Day);
                        DateTime max = min.AddDays(1);
                        queryable = queryable.Where(e => e.date < min || e.date >= max);
                        break;
                }
            }
            return queryable;
        }
    }
}
